import pytest
from dataclasses import dataclass


# 模拟API响应结构
@dataclass
class ApiResponse:
    success: bool
    request_id: str = ""
    error_code: str = ""
    error_msg: str = ""


# 模拟CVM客户端（无真实网络调用）
class CvmClient:
    def __init__(self):
        # 预定义错误场景
        self.error_scenarios = {
            "invalid_instance_id": ApiResponse(
                success=False,
                error_code="InvalidInstanceId.NotFound",
                error_msg="The specified instance does not exist."
            ),
            "invalid_security_group": ApiResponse(
                success=False,
                error_code="InvalidSecurityGroupId.NotFound",
                error_msg="The specified security group does not exist."
            ),
            "invalid_host_name": ApiResponse(
                success=False,
                error_code="InvalidParameterValue.HostName",
                error_msg="Invalid host name format."
            ),
            "batch_limit_exceeded": ApiResponse(
                success=False,
                error_code="LimitExceeded.BatchOperationLimit",
                error_msg="Exceeded maximum batch operation limit."
            ),
            "conflict_parameters": ApiResponse(
                success=False,
                error_code="InvalidParameter.MutexParameter",
                error_msg="Conflicting parameters detected."
            )
        }

    def modify_instance_attribute(
            self,
            instance_ids: list[str],
            instance_name: str = None,
            security_groups: list[str] = None,
            host_name: str = None
    ) -> ApiResponse:
        # 参数基础校验
        if not instance_ids:
            return ApiResponse(success=False, error_code="MissingParameter.InstanceId")

        if not all(isinstance(iid, str) and len(iid) >= 5 for iid in instance_ids):
            return ApiResponse(success=False, error_code="InvalidParameterValue.InstanceId")

        # 模拟资源存在性检查
        for iid in instance_ids:
            if iid == "ins-invalid":
                return self.error_scenarios["invalid_instance_id"]

        # 模拟实例状态检查
        if "ins-stopped" in instance_ids:
            return ApiResponse(
                success=False,
                error_code="InvalidInstanceStatus",
                error_msg="Instance is stopped"
            )

        # 模拟安全组有效性检查
        if security_groups and "sg-invalid" in security_groups:
            return self.error_scenarios["invalid_security_group"]

        # 主机名格式校验
        if host_name:
            if len(host_name) > 60 or host_name.startswith(('.', '-')):
                return self.error_scenarios["invalid_host_name"]

        # 参数互斥校验
        if instance_name and security_groups:
            return self.error_scenarios["conflict_parameters"]

        # 批量操作限制
        if security_groups and len(security_groups) > 5:
            return self.error_scenarios["batch_limit_exceeded"]

        # 默认成功响应
        return ApiResponse(
            success=True,
            request_id="mock-req-123456",
            error_code="",
            error_msg=""
        )


# 测试用例
class TestModifyInstanceAttribute:
    @pytest.fixture
    def client(self):
        return CvmClient()

    # 参数校验测试
    @pytest.mark.parametrize("instance_id,expected_error", [
        (None, "MissingParameter.InstanceId"),
        ("", "InvalidParameterValue.InstanceId"),
        ("ins-", "InvalidParameterValue.InstanceId"),
        (["ins-123", ""], "InvalidParameterValue.InstanceId")
    ])
    def test_invalid_parameters(self, client, instance_id, expected_error):
        resp = client.modify_instance_attribute(instance_ids=instance_id)
        assert not resp.success
        assert resp.error_code == expected_error

    # 资源状态测试
    @pytest.mark.parametrize("instance_id,expected_error", [
        ("ins-invalid", "InvalidInstanceId.NotFound"),
        ("ins-stopped", "InvalidInstanceStatus")  # 模拟实例状态错误
    ])
    def test_resource_status(self, client, instance_id, expected_error):
        resp = client.modify_instance_attribute(instance_ids=[instance_id])
        assert not resp.success
        assert resp.error_code == expected_error

    # 安全组测试
    @pytest.mark.parametrize("security_groups,expected_error", [
        (["sg-invalid"], "InvalidSecurityGroupId.NotFound"),
        (["sg-12345"], None)  # 无错误
    ])
    def test_security_groups(self, client, security_groups, expected_error):
        resp = client.modify_instance_attribute(
            instance_ids=["ins-123"],
            security_groups=security_groups
        )
        if expected_error:
            assert not resp.success
            assert resp.error_code == expected_error
        else:
            assert resp.success

    # 主机名测试
    @pytest.mark.parametrize("host_name,expected_error", [
        (".invalid", "InvalidParameterValue.HostName"),
        ("-invalid", "InvalidParameterValue.HostName"),
        ("too-long-hostname-" + "a" * 60, "InvalidParameterValue.HostName"),
        ("valid-hostname", None)
    ])
    def test_host_name_validation(self, client, host_name, expected_error):
        resp = client.modify_instance_attribute(
            instance_ids=["ins-123"],
            host_name=host_name
        )
        if expected_error:
            assert not resp.success
            assert resp.error_code == expected_error
        else:
            assert resp.success

    # 业务逻辑测试
    def test_conflict_parameters(self, client):
        resp = client.modify_instance_attribute(
            instance_ids=["ins-123"],
            instance_name="test-name",
            security_groups=["sg-123"]
        )
        assert not resp.success
        assert resp.error_code == "InvalidParameter.MutexParameter"

    def test_batch_limit(self, client):
        resp = client.modify_instance_attribute(
            instance_ids=["ins-123"],
            security_groups=["sg-1"] * 6  # 超过5个安全组
        )
        assert not resp.success
        assert resp.error_code == "LimitExceeded.BatchOperationLimit"

    # 正常用例
    def test_success_case(self, client):
        resp = client.modify_instance_attribute(
            instance_ids=["ins-123"],
            instance_name="new-name",
            security_groups=["sg-123"],
            host_name="valid-host"
        )
        assert resp.success
        assert resp.request_id.startswith("mock-req-")

    # 异常场景测试
    def test_unexpected_error(self, client):
        # 模拟未知错误
        original_modify = client.modify_instance_attribute

        def mock_modify(*args, **kwargs):
            return ApiResponse(success=False, error_code="UnexpectedError")

        client.modify_instance_attribute = mock_modify
        resp = client.modify_instance_attribute(instance_ids=["ins-123"])
        assert not resp.success
        assert resp.error_code == "UnexpectedError"
        client.modify_instance_attribute = original_modify


# 执行测试
if __name__ == "__main__":
    pytest.main(["-v", "--capture=no"])